package com.zlm.user.emp.service.impl;

import com.zlm.common.base.AbstractBaseService;
import com.zlm.common.constants.CodeConstants;
import com.zlm.common.constants.NumberConstants;
import com.zlm.common.constants.PunchConstants;
import com.zlm.common.dto.userEmp.EmpPunchDto;
import com.zlm.common.dto.userEmp.EmpPunchParamsDto;
import com.zlm.common.dto.userEmp.EmpPunchResponseDto;
import com.zlm.common.entity.EmpPunchDO;
import com.zlm.common.entity.OrgWorkTimeDO;
import com.zlm.common.entity.OrgWorkTimeRulesDO;
import com.zlm.common.entity.SysOrgDO;
import com.zlm.common.enums.PunchTypeEnum;
import com.zlm.common.util.DateTimeUtils;
import com.zlm.common.util.MathCommonUtils;
import com.zlm.user.emp.dao.*;
import com.zlm.user.emp.service.EmpPunchService;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.*;

/**
 * 员工考勤服务
 * @Author: duyufu
 * @Date: 2019/5/28 11:51
 */
@Service
public class EmpPunchServiceImpl extends AbstractBaseService<EmpPunchDao, EmpPunchDO> implements EmpPunchService {

    @Autowired
    @Override
    public void setBaseDao(EmpPunchDao empPunchDao) {
        this.baseDao = empPunchDao;
    }

    @Autowired
    private OrgWorkTimeRulesDao orgWorkTimeRulesDao;
    @Autowired
    private OrgWorkTimeDao orgWorkTimeDao;
    @Autowired
    private SysOrgDao sysOrgDao;
    @Autowired
    private OrgEntryDao orgEntryDao;

    /**
     * 获取指定日期考勤记录
     * @param empId
     * @param punchDate
     * @param workTimeId
     * @return
     */
    @Override
    public EmpPunchResponseDto queryEmpPunch(Integer empId, String punchDate, Integer workTimeId) {
        //封装返回信息
        EmpPunchResponseDto responseDto = new EmpPunchResponseDto();
        Date date = DateTimeUtils.string1Date(punchDate);
        Map<String,Object> map = new HashMap<>(2);
        map.put("empId",empId);
        map.put("punchDate", date);
        //获取考勤记录
        List<EmpPunchDO> empPunchList = baseDao.queryEmpPunchByMap(map);

        OrgWorkTimeDO workTimeDO = null;
        //判断日期是否今天
        if (DateTimeUtils.isSameDay(date, new Date())) {
            //获取（校验）班制
            workTimeDO = getWorkTime(empId, workTimeId, empPunchList, map);
        }

        if (workTimeDO == null) {
            //如果班制id不正确或者获取不到班制，直接返回
            workTimeDO = new OrgWorkTimeDO();
            workTimeDO.setClassSystem(PunchConstants.NO_WORKTIME);
            responseDto.setCurrentPunchType(PunchConstants.PUNCH_TYPE_0);
            responseDto.setCurrentPunchTypeName(PunchTypeEnum.getNameByCode(PunchConstants.PUNCH_TYPE_0));
        } else {
            //获取当前打卡类型
            int currentPunchType = getCurrentPunchType(punchDate, workTimeDO, empPunchList);
            //构建考勤列表
            List<Map<String,Object>> punchList = buildPunchList(empPunchList, workTimeDO.getWorkTimeId());
            //查询企业信息
            Map<String,Object> orgMap = new HashMap<>(1);
            orgMap.put("empId",empId);
            Map<String,Object> sysOrgMap = sysOrgDao.queryOrgByMap(orgMap);
            BigDecimal lng = null;
            BigDecimal lat = null;
            String address = "";
            if (sysOrgMap == null) {
                currentPunchType = PunchConstants.PUNCH_TYPE_0;
            } else {
                lng = (BigDecimal) sysOrgMap.get("longitude");
                lat = (BigDecimal) sysOrgMap.get("latitude");
                address = (String) sysOrgMap.get("address");
            }
            //封装返回信息
            responseDto.setWorkTimeId(workTimeDO.getWorkTimeId());
            responseDto.setSystemTime(DateTimeUtils.getCurrentDateTime());
            responseDto.setCurrentPunchType(currentPunchType);
            responseDto.setCurrentPunchTypeName(PunchTypeEnum.getNameByCode(currentPunchType));
            responseDto.setClassSystem(workTimeDO.getClassSystem());
            responseDto.setPunchRange(workTimeDO.getPunchRange());
            responseDto.setLongitude(lng);
            responseDto.setLatitude(lat);
            responseDto.setAddress(address);
            responseDto.setPunchList(punchList);
        }
        return responseDto;
    }

    /**
     * 考勤统计
     * @param empId
     * @param date
     * @return
     * @throws ParseException
     */
    @Override
    public EmpPunchDto countEmpPunch(Integer empId, String date) throws ParseException {
        //获取日期月份的起始日期
        Pair<Date, Date> pair = DateTimeUtils.getMonthStartAndEndDate(date);
        Date beginDate = pair.getLeft();
        Date endDate = pair.getRight();
        Date currentDate = new Date();
        if (currentDate.before(endDate)) {
            endDate = currentDate;
        }
        //获取起始时间相差的天数
        int days = DateTimeUtils.differentDaysByMillisecond(beginDate, endDate);
        //一次获取一个月的考勤记录
        List<EmpPunchDO> empPunchList = baseDao.selectEmpPunchList(empId,beginDate,endDate);
        Map<String,Object> map = new HashMap<>(2);
        map.put("empId",empId);
        //无打卡记录天数
        int countNoPunchDay = 0;
        //考勤正常天数
        int countPunchNormalDay = 0;
        //未签到次数
        int countWorkUpNoPunch = 0;
        //未签退次数
        int countWorkOffNoPunch = 0;
        //上班迟到次数
        int countWorkUpLaterTimes = 0;
        //下班早退次数
        int countWorkOffEarlyTimes = 0;
        //区域外次数
        int countAddressExceptionTimes = 0;

        List<Map<String,Object>> punchList = new ArrayList<>();
        List<Map<String,Object>> punchCount = new ArrayList<>();

        for (int i = 0;i <= days;i++) {
            Map<String,Object> punchMap = new HashMap<>(2);
            punchMap.put("punchDate",DateTimeUtils.date2String(beginDate));
            List<EmpPunchDO> punchDayList = new ArrayList<>();
            for (EmpPunchDO empPunchDO : empPunchList) {
                if (DateTimeUtils.isSameDay(beginDate,empPunchDO.getPunchDate())) {
                    punchDayList.add(empPunchDO);
                }
            }
            if (null == punchDayList || punchDayList.size() <= 0) {
                //表示当天无考勤
                countNoPunchDay++;
                punchMap.put("status",0);
            }else {
                EmpPunchDO workUp = punchDayList.get(0);
                EmpPunchDO workOff = punchDayList.get(1);
                if (workUp.getStatus().intValue() == PunchConstants.PUNCH_STATUS_NORMAL
                    && workOff.getStatus().intValue() == PunchConstants.PUNCH_STATUS_NORMAL ) {
                    countPunchNormalDay++;
                    punchMap.put("status",1);
                }else {
                    if (workUp.getStatus().intValue() == PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE) {
                        countWorkUpNoPunch++;
                    }else if (workUp.getStatus().intValue() == PunchConstants.PUNCH_STATUS_EXCEPTION_CODE){
                        if (workUp.getTimeError().intValue() == PunchConstants.PUNCH_LATE_EXCEPTION) {
                            countWorkUpLaterTimes++;
                        }else if (workUp.getAddressError().intValue() == PunchConstants.PUNCH_ADDRESS_EXCEPTION) {
                            countAddressExceptionTimes++;
                        }
                    }
                    if (workOff.getStatus().intValue() == PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE) {
                        countWorkOffNoPunch++;
                    }else if (workOff.getStatus().intValue() == PunchConstants.PUNCH_STATUS_EXCEPTION_CODE) {
                        if (workOff.getTimeError().intValue() == PunchConstants.PUNCH_LATE_EXCEPTION) {
                            countWorkOffEarlyTimes++;
                        }else if (workOff.getAddressError().intValue() == PunchConstants.PUNCH_ADDRESS_EXCEPTION) {
                            countAddressExceptionTimes++;
                        }
                    }
                    punchMap.put("status",2);
                }
            }
            punchList.add(punchMap);
            //给开始时间加一天，统计下一天的考勤
            beginDate = DateTimeUtils.getDateAfter(beginDate,1);
        }
        EmpPunchDto empPunchDto = new EmpPunchDto();
        Map<String,Object> countMap;
        if (countPunchNormalDay > 0) {
            countMap = new HashMap<>(3);
            countMap.put("text","正常");
            countMap.put("value",countPunchNormalDay);
            countMap.put("unit","天");
            punchCount.add(countMap);
        }
        if (countNoPunchDay > 0 ) {
            countMap = new HashMap<>(3);
            countMap.put("text","无考勤");
            countMap.put("value",countNoPunchDay);
            countMap.put("unit","天");
            punchCount.add(countMap);
        }
        if (countAddressExceptionTimes > 0 ) {
            countMap = new HashMap<>(3);
            countMap.put("text","范围外");
            countMap.put("value",countAddressExceptionTimes);
            countMap.put("unit","次");
            punchCount.add(countMap);
        }
        if (countWorkUpNoPunch > 0) {
            countMap = new HashMap<>(3);
            countMap.put("text","未签到");
            countMap.put("value",countWorkUpNoPunch);
            countMap.put("unit","次");
            punchCount.add(countMap);
        }
        if (countWorkOffNoPunch > 0) {
            countMap = new HashMap<>(3);
            countMap.put("text","未签退");
            countMap.put("value",countWorkOffNoPunch);
            countMap.put("unit","次");
            punchCount.add(countMap);
        }
        if (countWorkUpLaterTimes > 0) {
            countMap = new HashMap<>(3);
            countMap.put("text","迟到");
            countMap.put("value",countWorkUpLaterTimes);
            countMap.put("unit","次");
            punchCount.add(countMap);
        }
        if (countWorkOffEarlyTimes > 0 ) {
            countMap = new HashMap<>(3);
            countMap.put("text","早退");
            countMap.put("value",countWorkOffEarlyTimes);
            countMap.put("unit","次");
            punchCount.add(countMap);
        }
        empPunchDto.setPunchList(punchList);
        empPunchDto.setPunchCount(punchCount);
        return empPunchDto;
    }

    /**
     * 考勤明细
     * @param empId
     * @param date
     * @return
     * @throws Exception
     */
    @Override
    public List<Map<String, Object>> punchDetail(Integer empId, String date) throws Exception {
        //获取日期月份的起始日期
        Pair<Date, Date> pair = DateTimeUtils.getMonthStartAndEndDate(date);
        Date beginDate = pair.getLeft();
        Date endDate = pair.getRight();
        //获取起始时间相差的天数
        int days = DateTimeUtils.differentDaysByMillisecond(beginDate, endDate);
        List<Map<String,Object>> punchMapList = new ArrayList<>();
        //一次获取一个月的考勤记录
        List<EmpPunchDO> empPunchList = baseDao.selectEmpPunchList(empId,beginDate,endDate);
        for (int i = 0;i <= days;i++) {
            Map<String,Object> punchMap = new HashMap<>(7);
            punchMap.put("punchDate",DateTimeUtils.datetime4String(beginDate));
            punchMap.put("punchWeek",DateTimeUtils.getWeek(beginDate));
            List<EmpPunchDO> punchDayList = new ArrayList<>();
            for (EmpPunchDO empPunchDO : empPunchList) {
                if (DateTimeUtils.isSameDay(beginDate,empPunchDO.getPunchDate())) {
                    punchDayList.add(empPunchDO);
                }
            }
            if (null == punchDayList || punchDayList.size() <= 0) {
                punchMap.put("punchTime","");
                punchMap.put("punchType","");
                punchMap.put("timeError","");
                punchMap.put("addressError","");
                punchMap.put("status",PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE);
                punchMapList.add(punchMap);
            }else {
                for (EmpPunchDO empPunchDO : punchDayList) {
                    //考勤为上班
                    if (PunchConstants.PUNCH_TYPE_1 == empPunchDO.getPunchType()) {
                        Map<String,Object> workUp = new HashMap<>(7);
                        workUp.put("punchDate",DateTimeUtils.datetime4String(beginDate));
                        workUp.put("punchWeek",DateTimeUtils.getWeek(beginDate));
                        workUp.put("punchTime",
                                empPunchDO.getPunchTime() == null ? PunchConstants.PUNCH_STATUS_NO_WORK_UP :
                                        DateTimeUtils.datetime3String(empPunchDO.getPunchTime()));
                        workUp.put("punchType",PunchConstants.WORK_UP);
                        if (empPunchDO.getTimeError().intValue() == 1) {
                            workUp.put("timeError",PunchConstants.PUNCH_LATE);
                        }else {
                            workUp.put("timeError","");
                        }
                        if (empPunchDO.getAddressError().intValue() == 1) {
                            workUp.put("addressError",PunchConstants.PUNCH_OUT_ZONE);
                        }else {
                            workUp.put("addressError","");
                        }
                        workUp.put("status",empPunchDO.getStatus());
                        punchMapList.add(workUp);
                    }else if (PunchConstants.PUNCH_TYPE_2 == empPunchDO.getPunchType()) {
                        Map<String,Object> workOff = new HashMap<>(7);
                        workOff.put("punchDate",DateTimeUtils.datetime4String(beginDate));
                        workOff.put("punchWeek",DateTimeUtils.getWeek(beginDate));
                        workOff.put("punchTime",
                                empPunchDO.getPunchTime() == null ? PunchConstants.PUNCH_STATUS_NO_WORK_OFF :
                                        DateTimeUtils.datetime3String(empPunchDO.getPunchTime()));
                        workOff.put("punchType",PunchConstants.WORK_OFF);
                        if (empPunchDO.getTimeError().intValue() == 1) {
                            workOff.put("timeError",PunchConstants.PUNCH_LEAVE_EARLY);
                        }else {
                            workOff.put("timeError","");
                        }
                        if (empPunchDO.getAddressError().intValue() == 1) {
                            workOff.put("addressError",PunchConstants.PUNCH_OUT_ZONE);
                        }else {
                            workOff.put("addressError","");
                        }
                        workOff.put("status",empPunchDO.getStatus());
                        punchMapList.add(workOff);
                    }else {
                        punchMap.put("punchTime","");
                        punchMap.put("punchType","");
                        punchMap.put("timeError","");
                        punchMap.put("addressError","");
                        punchMap.put("status",PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE);
                        punchMapList.add(punchMap);
                    }
                }
            }
            //给开始时间加一天，统计下一天的考勤
            beginDate = DateTimeUtils.getDateAfter(beginDate,1);
        }
        return punchMapList;
    }

    /**
     * 考勤打卡
     * @param empPunchParamsDto
     * @throws RuntimeException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void empPunch(EmpPunchParamsDto empPunchParamsDto) throws RuntimeException{
        Date punchDate = DateTimeUtils.string1Date(empPunchParamsDto.getPunchDate());
        Map<String,Object> map = new HashMap<>();
        Integer empId = empPunchParamsDto.getEmpId();
        map.put("empId",empId);
        map.put("punchDate",punchDate);
        map.put("workTimeId",empPunchParamsDto.getOrgWorkTimeId());
        //根据打卡日期查询考勤表
        List<EmpPunchDO> empPunchList = baseDao.queryEmpPunchByMap(map);

        //判断班制id是否属于用户所在企业
        int count = orgWorkTimeDao.isExistWorkTimeWithOrg(empPunchParamsDto.getEmpId(),empPunchParamsDto.getOrgWorkTimeId());
        if (count == 0) {
            throw new RuntimeException(CodeConstants.CODE_PARAMS);
        }
        //查出员工班制信息
        OrgWorkTimeDO workTimeDO = orgWorkTimeDao.queryWorkTimeById(empPunchParamsDto.getOrgWorkTimeId());
        //获取当前时间的时间戳
        Long currentTimestamp = DateTimeUtils.getCurrentTimestamp().getTime();
        String currentTimeString = DateTimeUtils.timeStamp2Date(String.valueOf(currentTimestamp), null);
        Date currentTimeDate = DateTimeUtils.dateString2Date(currentTimeString, DateTimeUtils.FORMAT_DATETIME_DEFAULT);
        //获取完整上班时间
        String workUpString = empPunchParamsDto.getPunchDate().concat(" ").concat(DateTimeUtils.datetime3String(workTimeDO.getWorkUp()));
        //获取应上班时间戳
        Long workUpTimestamp = Timestamp.valueOf(workUpString).getTime();
        //获取完整下班时间
        String workOffString = empPunchParamsDto.getPunchDate().concat(" ").concat(DateTimeUtils.datetime3String(workTimeDO.getWorkOff()));
        //获取应下班时间戳
        Long workOffTimestamp = Timestamp.valueOf(workOffString).getTime();
        //比较班制表中的上下班时间，如果下班时间小于上班时间，应当在下班时间上加一天
        if (workUpTimestamp > workOffTimestamp) {
            workOffTimestamp = workOffTimestamp + PunchConstants.TIMESTAMP_ONE_DAY;
        }
        //获取当前打卡类型
        Integer currentPunchType = getCurrentPunchType(empPunchParamsDto.getPunchDate(), workTimeDO, empPunchList);
        //判断当前打卡类型为允许打卡
        if (currentPunchType != PunchConstants.PUNCH_TYPE_0) {
            Integer entryId = orgEntryDao.queryEntryId(empPunchParamsDto.getEmpId());
            SysOrgDO org = sysOrgDao.queryOrgById(workTimeDO.getOrgId());
            //查看打卡范围是否正常
            boolean inCircle = MathCommonUtils.isInCircle(empPunchParamsDto.getPunchLng().doubleValue(),
                    empPunchParamsDto.getPunchLat().doubleValue(),
                    org.getLongitude().doubleValue(), org.getLatitude().doubleValue(),
                    workTimeDO.getPunchRange().toString());
            boolean b = true;
            if (null == empPunchList || empPunchList.size()<= 0) {
                List<EmpPunchDO> punchList = new ArrayList<>(2);
                EmpPunchDO workUp = new EmpPunchDO();
                workUp.setWorkTimeId(empPunchParamsDto.getOrgWorkTimeId());
                workUp.setPunchType(PunchConstants.PUNCH_TYPE_1);
                workUp.setPunchDate(punchDate);
                workUp.setEntryId(entryId);
                workUp.setShouldPunchTime(DateTimeUtils.dateString2Date(workUpString,DateTimeUtils.FORMAT_DATETIME_DEFAULT));
                if (currentPunchType == PunchConstants.PUNCH_TYPE_1) {
                    workUp.setPunchLng(empPunchParamsDto.getPunchLng());
                    workUp.setPunchLat(empPunchParamsDto.getPunchLat());
                    workUp.setPunchAddress(empPunchParamsDto.getPunchAddress());
                    workUp.setPunchTime(currentTimeDate);
                    if (currentTimestamp > workUpTimestamp) {
                        //迟到
                        workUp.setTimeError(PunchConstants.PUNCH_LATE_EXCEPTION);
                        b = false;
                    }else {
                        workUp.setTimeError(PunchConstants.PUNCH_TIME_NORMAL);
                    }
                    if (!inCircle) {
                        workUp.setAddressError(PunchConstants.PUNCH_ADDRESS_EXCEPTION);
                        b = false;
                    }else {
                        workUp.setAddressError(PunchConstants.PUNCH_ADDRESS_NORMAL);
                    }
                    if (!b) {
                        workUp.setStatus(PunchConstants.PUNCH_STATUS_EXCEPTION_CODE);
                    }else {
                        workUp.setStatus(PunchConstants.PUNCH_STATUS_NORMAL);
                    }
                }else {
                    workUp.setStatus(PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE);
                }
                punchList.add(workUp);
                EmpPunchDO workOff = new EmpPunchDO();
                workOff.setWorkTimeId(empPunchParamsDto.getOrgWorkTimeId());
                workOff.setPunchType(PunchConstants.PUNCH_TYPE_2);
                workOff.setPunchDate(punchDate);
                workOff.setEntryId(entryId);
                workOff.setShouldPunchTime(DateTimeUtils.dateString2Date(workOffString, DateTimeUtils.FORMAT_DATETIME_DEFAULT));
                if (currentPunchType == PunchConstants.PUNCH_TYPE_2) {
                    workOff.setPunchLng(empPunchParamsDto.getPunchLng());
                    workOff.setPunchLat(empPunchParamsDto.getPunchLat());
                    workOff.setPunchAddress(empPunchParamsDto.getPunchAddress());
                    workOff.setPunchTime(currentTimeDate);
                    if (currentTimestamp < workOffTimestamp) {
                        //早退
                        workOff.setTimeError(PunchConstants.PUNCH_LATE_EXCEPTION);
                        b = false;
                    }else {
                        workOff.setTimeError(PunchConstants.PUNCH_TIME_NORMAL);
                    }
                    if (!inCircle) {
                        workOff.setAddressError(PunchConstants.PUNCH_ADDRESS_EXCEPTION);
                        b = false;
                    }else {
                        workOff.setAddressError(PunchConstants.PUNCH_ADDRESS_NORMAL);
                    }
                    if (!b) {
                        workOff.setStatus(PunchConstants.PUNCH_STATUS_EXCEPTION_CODE);
                    }else {
                        workOff.setStatus(PunchConstants.PUNCH_STATUS_NORMAL);
                    }
                }else {
                    workOff.setStatus(PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE);
                }
                punchList.add(workOff);
                baseDao.insertEmpPunchList(punchList);
            }else {
                if (empPunchList.size() == PunchConstants.PUNCH_TIMES) {
                    if (currentPunchType == PunchConstants.PUNCH_TYPE_1) {
                        //打卡类型-上班
                        EmpPunchDO empPunchDO = empPunchList.get(0);
                        empPunchDO.setPunchAddress(empPunchParamsDto.getPunchAddress());
                        empPunchDO.setPunchLng(empPunchParamsDto.getPunchLng());
                        empPunchDO.setPunchLat(empPunchParamsDto.getPunchLat());
                        empPunchDO.setPunchTime(currentTimeDate);
                        if (currentTimestamp > workUpTimestamp) {
                            //迟到
                            empPunchDO.setTimeError(PunchConstants.PUNCH_LATE_EXCEPTION);
                            b = false;
                        }else {
                            empPunchDO.setTimeError(PunchConstants.PUNCH_TIME_NORMAL);
                        }
                        if (!inCircle) {
                            empPunchDO.setAddressError(PunchConstants.PUNCH_ADDRESS_EXCEPTION);
                            b = false;
                        }else {
                            empPunchDO.setAddressError(PunchConstants.PUNCH_ADDRESS_NORMAL);
                        }
                        if (!b) {
                            empPunchDO.setStatus(PunchConstants.PUNCH_STATUS_EXCEPTION_CODE);
                        }else {
                            empPunchDO.setStatus(PunchConstants.PUNCH_STATUS_NORMAL);
                        }
                        baseDao.updateEmpPunch(empPunchDO);
                    }else {
                        //打卡类型-下班
                        EmpPunchDO empPunchDO = empPunchList.get(1);
                        empPunchDO.setPunchAddress(empPunchParamsDto.getPunchAddress());
                        empPunchDO.setPunchLng(empPunchParamsDto.getPunchLng());
                        empPunchDO.setPunchLat(empPunchParamsDto.getPunchLat());
                        empPunchDO.setPunchTime(currentTimeDate);
                        if (currentTimestamp < workOffTimestamp) {
                            //早退
                            empPunchDO.setTimeError(PunchConstants.PUNCH_LATE_EXCEPTION);
                            b = false;
                        }else {
                            empPunchDO.setTimeError(PunchConstants.PUNCH_TIME_NORMAL);
                        }
                        if (!inCircle) {
                            empPunchDO.setAddressError(PunchConstants.PUNCH_ADDRESS_EXCEPTION);
                            b = false;
                        }else {
                            empPunchDO.setAddressError(PunchConstants.PUNCH_ADDRESS_NORMAL);
                        }
                        if (!b) {
                            empPunchDO.setStatus(PunchConstants.PUNCH_STATUS_EXCEPTION_CODE);
                        }else {
                            empPunchDO.setStatus(PunchConstants.PUNCH_STATUS_NORMAL);
                        }
                        baseDao.updateEmpPunch(empPunchDO);
                    }
                }else {
                    throw new RuntimeException(CodeConstants.CODE_FAIL);
                }
            }
        }
    }

    /**
     * 获取当前打卡类型（0-不允许打卡，1-允许上班打卡，2-允许下班打卡）
     * @param punchDate
     * @param workTimeDO
     * @param empPunchList
     * @return
     */
    private int getCurrentPunchType(String punchDate, OrgWorkTimeDO workTimeDO, List<EmpPunchDO> empPunchList) {
        //获取当前时间的时间戳
        Long currentTimestamp = DateTimeUtils.getCurrentTimestamp().getTime();
        //获取上班时间戳
        Long workUpTimestamp =
                Timestamp.valueOf(punchDate.concat(" ").concat(DateTimeUtils.datetime3String(workTimeDO.getWorkUp()))).getTime();
        //获取下班时间戳
        Long workOffTimestamp =
                Timestamp.valueOf(punchDate.concat(" ").concat(DateTimeUtils.datetime3String(workTimeDO.getWorkOff()))).getTime();
        //比较班制表中的上下班时间，如果下班时间小于上班时间，应当在下班时间上加一天
        if (workUpTimestamp > workOffTimestamp) {
            workOffTimestamp = workOffTimestamp + PunchConstants.TIMESTAMP_ONE_DAY;
        }
        Long workTimestamp = workOffTimestamp - workUpTimestamp;
        //最早打卡时间
        Long tempWorkUp = workUpTimestamp - PunchConstants.FORWARD_HOUR * PunchConstants.TIMESTAMP_ONE_HOUR;
        //最晚打卡时间
        Long tempWorkOff = workOffTimestamp + PunchConstants.BACKWARD_HOUR * PunchConstants.TIMESTAMP_ONE_HOUR;
        //上下班中间时间
        Long middleWorkTime = workUpTimestamp + workTimestamp/ NumberConstants.NUM_2;
        //不允许打卡
        int currentPunchType = PunchConstants.PUNCH_TYPE_0;
        if (currentTimestamp >= tempWorkUp && currentTimestamp < middleWorkTime) {
            //上班打卡
            currentPunchType = PunchConstants.PUNCH_TYPE_1;
        } else if (currentTimestamp >= middleWorkTime && currentTimestamp <= tempWorkOff) {
            //下班打卡
            currentPunchType = PunchConstants.PUNCH_TYPE_2;
        }
        if (currentPunchType != PunchConstants.PUNCH_TYPE_0 && empPunchList != null) {
            for (EmpPunchDO punch : empPunchList) {
                if (punch.getPunchType().intValue() == currentPunchType &&
                        punch.getStatus() > PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE &&
                        punch.getWorkTimeId().equals(workTimeDO.getWorkTimeId())) {
                    //如果对应的打卡类型（同一班制下）已经有打卡记录
                    //如果上班已打卡，则允许下班打卡；如果下班已打卡，则不允许打卡
                    currentPunchType = currentPunchType == PunchConstants.PUNCH_TYPE_1 ?
                            PunchConstants.PUNCH_TYPE_2 : PunchConstants.PUNCH_TYPE_0;
                }
            }
        }
        return currentPunchType;
    }

    /**
     * 获取指定日期考勤记录（仅考勤）
     * @param empId
     * @param punchDate
     * @return
     */
    @Override
    public List<Map<String,Object>> queryEmpPunchDay(String empId, String punchDate) {
        Date date = DateTimeUtils.string1Date(punchDate);
        Map<String,Object> map = new HashMap<>();
        map.put("empId",empId);
        map.put("punchDate",date);
        //查询考勤信息
        List<EmpPunchDO> empPunchList = baseDao.queryEmpPunchByMap(map);
        List<Map<String,Object>> punchList = new ArrayList<>();
        Map<String,Object> workMap;
        if (null != empPunchList) {
            for (EmpPunchDO punch : empPunchList) {
                workMap = new HashMap<>(7);
                workMap.put("statusName","");
                workMap.put("timeError","");
                workMap.put("addressError","");

                if (punch.getPunchType().intValue() == PunchConstants.PUNCH_TYPE_1) {
                    workMap.put("punchType",PunchConstants.WORK_UP);
                }
                if (punch.getPunchType().intValue() == PunchConstants.PUNCH_TYPE_2) {
                    workMap.put("punchType",PunchConstants.WORK_OFF);
                }

                if (punch.getStatus().intValue() == PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE) {
                    workMap.put("statusName", punch.getPunchType().intValue() == PunchConstants.PUNCH_TYPE_1 ?
                            PunchConstants.PUNCH_STATUS_NO_WORK_UP : PunchConstants.PUNCH_STATUS_NO_WORK_OFF);
                } else {
                    if (punch.getStatus().intValue() == PunchConstants.PUNCH_STATUS_EXCEPTION_CODE) {
                        if (punch.getTimeError() == PunchConstants.PUNCH_LATE_EXCEPTION) {
                            workMap.put("timeError", punch.getPunchType().intValue() == PunchConstants.PUNCH_TYPE_1 ?
                                    PunchConstants.PUNCH_LATE : PunchConstants.PUNCH_LEAVE_EARLY);
                        }

                        if (punch.getAddressError() == PunchConstants.PUNCH_ADDRESS_EXCEPTION) {
                            workMap.put("addressError", PunchConstants.PUNCH_OUT_ZONE);
                        }
                    }
                }
                workMap.put("status", punch.getStatus());
                workMap.put("punchTime", punch.getPunchTime() == null ? "" :
                        DateTimeUtils.datetime3String(punch.getPunchTime()));
                workMap.put("punchAddress", punch.getPunchAddress());
                punchList.add(workMap);
            }
        }

        return punchList;
    }

    /**
     * 构建考勤列表
     * @param empPunchList
     * @return
     */
    private List<Map<String,Object>> buildPunchList(List<EmpPunchDO> empPunchList, Integer workTimeId) {
        List<Map<String,Object>> punchList = new ArrayList<>();
        if (empPunchList != null) {
            Map<String,Object> tmpMap;
            for (EmpPunchDO punch : empPunchList) {
                if (workTimeId != null && workTimeId.intValue() != punch.getWorkTimeId().intValue()) {
                    continue;
                }
                String punchTypeName = punch.getPunchType() == PunchConstants.PUNCH_TYPE_1 ?
                        PunchConstants.WORK_UP_TIME : PunchConstants.WORK_OFF_TIME;
                tmpMap = new HashMap<>(6);
                tmpMap.put("punchType", punch.getPunchType());
                tmpMap.put("punchTypeName", punchTypeName);
                tmpMap.put("shouldPunchTime", punch.getShouldPunchTime() == null ? "" :
                        DateTimeUtils.datetime5String(punch.getShouldPunchTime(),
                                DateTimeUtils.FORMAT_DATETIME_HOUR_MIN));
                tmpMap.put("punchTime", punch.getPunchTime() == null ? "" :
                        DateTimeUtils.datetime3String(punch.getPunchTime()));
                tmpMap.put("punchAddress", punch.getPunchAddress());
                tmpMap.put("status", punch.getStatus());
                if (null != punch.getPunchTime() && DateTimeUtils.isTomorrow(punch.getPunchDate(),
                        punch.getPunchTime())) {
                    tmpMap.put("isTomorrow",true);
                } else {
                    tmpMap.put("isTomorrow",false);
                }
                tmpMap.put("actionName", punch.getStatus() != PunchConstants.PUNCH_STATUS_NO_PUNCH_CODE ?
                        PunchConstants.PUNCH_TIME : (punch.getPunchType().intValue() == PunchConstants.PUNCH_TYPE_1 ? PunchConstants.PUNCH_STATUS_NO_WORK_UP : PunchConstants.PUNCH_STATUS_NO_WORK_OFF));
                punchList.add(tmpMap);
            }
        }
        return punchList;
    }

    /**
     * 获取用户打卡班制
     * @param empId
     * @param workTimeId
     * @param empPunchList
     * @return
     */
    private OrgWorkTimeDO getWorkTime(Integer empId, Integer workTimeId,
                                      List<EmpPunchDO> empPunchList, Map<String,Object> map) {
        OrgWorkTimeDO workTimeDO = null;
        Integer orgWorkTimeId = null;

        //班制id为空时，从班制规则表和企业班制表中查找班制
        if (null == workTimeId) {
            if (null != empPunchList && empPunchList.size() > 0 ) {
                //如果当天存在考勤记录，以考勤记录对应的班制id为准
                orgWorkTimeId = empPunchList.get(0).getWorkTimeId();
            }else {
                //否则从班制规则表中查找班制
                OrgWorkTimeRulesDO workTimeRule = orgWorkTimeRulesDao.queryWorkTimeRulesByMaps(map);
                if (null != workTimeRule) {
                    orgWorkTimeId = workTimeRule.getWorkTimeId();
                }else {
                    workTimeDO = orgWorkTimeDao.queryWorkTimeByMaps(map);
                }
            }
        }else {
            //判断班制id是否属于用户所在企业
            int count = orgWorkTimeDao.isExistWorkTimeWithOrg(empId,workTimeId);
            if (count > 0) {
                orgWorkTimeId = workTimeId;
            }
        }

        if (orgWorkTimeId != null && workTimeDO == null) {
            workTimeDO = orgWorkTimeDao.queryWorkTimeById(orgWorkTimeId);
        }

        return workTimeDO;
    }

}
